package cn.yjh.client;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;

import cn.yjh.server.support.HttpStatus;
import cn.yjh.server.support.Request;
import cn.yjh.server.support.Response;

/**
 * @DESC:
 * @AUTHOR: YouJinhua
 * @DATE: 2021-12-09 20:51:13
 * @VERSION: 1.0
 */
public class SocketChannelRPCHandler<T>  extends RPCHandler {

    private static Logger logger = LoggerFactory.getLogger(SocketChannelRPCHandler.class);

    public SocketChannelRPCHandler(String host, int port,Class<T> clazz) {
        super(host,port,clazz);
    }
    @Override
    protected Object doInvoke(Object proxy, Method method, Object[] args)  {
        Selector selector = null;
        SocketChannel socketChannel = null;
        try {
            // 进行selector注册 1、先创建Selector
            selector = Selector.open();
            // 获取对应的channel并进行监听
            socketChannel = SocketChannel.open();
            // 设置为非阻塞模式
            socketChannel.configureBlocking(false);
            socketChannel.connect(new InetSocketAddress(host, port));
            // 2、进行selector注册 对连接完成和读取进行绑定
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
            // 获取访问就绪的通道  通过Selector的select（）方法可以选择已经准备就绪的通道 （这些通道包含你注册的兴趣事件）
            while(selector.select() > 0) {
                Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
                while (keys.hasNext()) {
                    SelectionKey key = keys.next();
                    if (key.isReadable()) {
                        SocketChannel readChannel = (SocketChannel) key.channel();
                        ByteBuffer result = ByteBuffer.allocate(1024);
                        readChannel.read(result);
                        logger.info("[ Read size: {} ]",result.position());
                        result.flip();
                        Response response = JSON.parseObject(result.array(),Response.class);
                        if(HttpStatus.OK == response.getStatus()){
                            return response.getData();
                        }else{
                           throw new RuntimeException(response.getMsg());
                        }
                    } else if (key.isConnectable()) {
                        SocketChannel sc = (SocketChannel)key.channel();
                        if (sc.finishConnect()) {
                            logger.info("connect server success !");
                            // 若已经连接成功 则进行数据传输
                           /* key.interestOps(SelectionKey.OP_WRITE);
                            Request.Invoker invoker = new Request.Invoker();
                            invoker.setClazzName(clazz.getName());
                            invoker.setMethodName(method.getName());
                            invoker.setParamsType(method.getParameterTypes());
                            invoker.setParams(args);
                            String string = JSON.toJSONString(invoker);
                            System.out.println(string);
                            byte[] bytes = JSON.toJSONBytes(invoker);
                            StringBuilder requestLineBuilder = new StringBuilder();
                            requestLineBuilder.append("RPC ").append("/"+method.getName()+" ").append("RPC/1.0\r\n\r\n");
                            byte[] requestLintBytes = requestLineBuilder.toString().getBytes(StandardCharsets.UTF_8);
                            ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length+requestLintBytes.length);
                            byteBuffer.put(requestLintBytes);
                            byteBuffer.put(bytes);
                            logger.info("[ Write size: {} ]",byteBuffer.position());
                            byteBuffer.flip();
                            sc.write(byteBuffer);
                            byteBuffer.clear();*/
                            sc.register(selector, SelectionKey.OP_READ);
                        } else {
                            logger.error("connect server failed !");
                        }
                    }
                }
            }
        } catch (IOException e) {
            logger.info("exception: {}",e);
            try {
                if(selector != null)
                    selector.close();
                if(socketChannel != null)
                    socketChannel.close();
            } catch (IOException e1) {
                logger.info("close socket channel exception: {}",e);
            }
        }
        return null;
    }
}
